<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Title</title>
</head>
<body>
<script>
  const State = {
    initial: 1,     // 初始状态
    tagOpen: 2,     // 标签开始
    tagName: 3,     // 标签名称
    text: 4,        // 文本
    tagEnd: 5,      // 结束标签
    tagEndName: 6,  // 结束标签名称
  }

  // 判断是否是字母
  function isAlpha(char) {
    return char >= 'a' && char <= 'z' || char >= 'A' && char <= 'Z' || (char.length === 0 && /\s+/.test(char)) || char === ' '
  }

  // 接收模板字符串，切割为 token 返回
  function tokenzie(str) {
    // 状态机
    let currentState = State.initial
    // 缓存字符
    const chars = []

    // 生成 Token 会储存在 Tokens 数组中，并作为函数返回值返回
    const tokens = []

    // 开启自动机
    while (str) {
      // 查看第一个字符
      const char = str[0]

      // 匹配当前状态
      switch (currentState) {
        case State.initial: {
          if (char === '<') {
            // 切换到 开始状态
            currentState = State.tagOpen
            // 消费字符 <
            str = str.slice(1)
          } else if (isAlpha(char)) {
            currentState = State.text

            chars.push(char)
            str = str.slice(1)
          }
          break
        }
        case State.tagOpen: {
          if (isAlpha(char)) {
            // 切换到 开始状态
            currentState = State.tagName

            chars.push(char)

            str = str.slice(1)
          } else if (char === '/') {
            currentState = State.tagEnd

            str = str.slice(1)
          }
          break
        }
        case State.tagName: {
          if (isAlpha(char)) {
            chars.push(char)

            str = str.slice(1)
          } else if (char === '>') {
            currentState = State.initial

            tokens.push({
              type: 'tag',
              content: chars.join('')
            })

            chars.length = 0

            str = str.slice(1)
          }
          break
        }
        case State.text: {
          if (isAlpha(char)) {
            chars.push(char)

            str = str.slice(1)
          } else if (char === '<') {
            currentState = State.tagOpen

            tokens.push({
              type: 'text',
              content: chars.join('')
            })

            chars.length = 0

            str = str.slice(1)
          }
          break
        }
        case State.tagEnd: {
          if (isAlpha(char)) {
            currentState = State.tagEndName

            chars.push(char)

            str = str.slice(1)
          }
          break
        }
        case State.tagEndName: {
          if (isAlpha(char)) {
            chars.push(char)

            str = str.slice(1)
          } else if (char === '>') {
            currentState = State.initial

            tokens.push({
              type: 'tagEnd',
              content: chars.join('')
            })

            chars.length = 0

            str = str.slice(1)
          }
          break
        }
      }
    }

    return tokens
  }
</script>

<script>
  const tokens = tokenzie('<p> Vue <img/> </p>')

  console.log(tokens)
</script>
</body>
</html>
